using System;
using System.Net;
using System.Threading;
using System.Reflection;
using System.Diagnostics;

using System.Security;
using System.Security.Principal;

namespace CSharpDistilled.Diagnostics
{
    /// <summary>
    /// Provides contextual information for the <see cref="T:ExceptionProvider"/> and <see cref="T:LoggingProvider"/> classes.
    /// </summary>
    public abstract class ContextBase
    {
        #region ContextBase Private Fields

        private Guid m_id;

        private Assembly m_assembly;

        private AppDomain m_appDomain;

        private Thread m_thread;

        private string m_operatingSystem;

        private int m_processorCount;

        private Process m_process;

        private IIdentity m_identity;

        private string m_machineName;

        private IPAddress m_address;

        #endregion

        #region protected ContextBase(Assembly assembly, AppDomain appDomain, Thread thread, Process process)

        /// <summary>
        /// Initializes a new instance of the <see cref="T:ContextBase"/> class.    
        /// </summary>
        /// <param name="assembly">The <see cref="T:System.Reflection.Assembly"/> to associate with this context.</param>
        /// <param name="appDomain">The <see cref="T:System.AppDomain"/> to associate with this context.</param>
        /// <param name="thread">The <see cref="T:System.Threading.Thread"/> to associate with this context.</param>
        /// <param name="process">The <see cref="T:System.Diagnostics.Process"/> associated with this context.</param>
        /// <param name="identity">The <see cref="T:System.Security.Principal.IIdentity"/> associated with this context.</param>
        protected ContextBase(Assembly assembly, AppDomain appDomain, Thread thread, Process process, IIdentity identity)
        {
            m_assembly = assembly;
            m_appDomain = appDomain;
            m_thread = thread;
            m_process = process;
            m_identity = identity;

            // establish a new context Id.
            m_id = Guid.NewGuid();

            // retrieve the host operating system.
            OperatingSystem operatingSystem = new CSharpDistilled.Diagnostics.OperatingSystem();

            if (operatingSystem == null || string.IsNullOrEmpty(operatingSystem.Name))
            {
                m_operatingSystem = Environment.OSVersion.VersionString;
            }
            else
            {
                m_operatingSystem = operatingSystem.Name;
            }

            // retrieve the processor count (essentially the total number of cores for multi-core CPU's).
            m_processorCount = Environment.ProcessorCount;

            // although the process includes the machine name it can be '.', if the process is running on the local machine, therefore we capture the machine name here also.
            m_machineName = Environment.MachineName;

            // retrieve a list of IP addresses for the given machine name.
            IPHostEntry entry = Dns.GetHostEntry(Environment.MachineName);

            // iterate through the IP addresses for an appropriate IP address, the iteration is done in reverse because the first address is usually '::1' which isn't very useful.
            for (int index = entry.AddressList.Length - 1; index >= 0; index--)
            {
                m_address = entry.AddressList[index];

                // ensure the address we've got isn't the local '::1' address and if there are more addresses contine the loop, otherwise break.
                if (string.Equals(m_address.ToString(), "::1") && index > 0)
                {
                    continue;
                }
                else
                {
                    break;
                }
            }
        }

        #endregion

        #region public Guid Id

        /// <summary>
        /// Gets the <see cref="T:System.Guid"/> identifier for this <see cref="T:System.Exception"/> or log entry.
        /// </summary>
        /// <value>The <see cref="T:System.Guid"/> identifier for this <see cref="T:System.Exception"/> or log entry.</value>
        public Guid Id
        {
            get
            {
                return m_id;
            }
        }

        #endregion

        #region public bool AssemblyDebugBuild

        /// <summary>
        /// Gets a <see cref="T:System.Boolean"/> value indicating whether the <see cref="T:System.Reflection.Assembly"/> within which the exception was thrown was built in Debug or Release mode.
        /// </summary>
        /// <value><c>true</c> if and only if the <see cref="T:System.Reflection.Assembly"/> was built in Debug mode, <c>false</c> otherwise.</value>
        public bool AssemblyDebugBuild
        {
            get
            {
                foreach (object attribute in m_assembly.GetCustomAttributes(false))
                {
                    DebuggableAttribute _attribute = attribute as DebuggableAttribute;

                    if (_attribute != null)
                    {
                        return _attribute.IsJITTrackingEnabled;
                    }
                }

                return false;
            }
        }

        #endregion

        #region public Assembly Assembly

        /// <summary>
        /// Gets the <see cref="T:System.Reflection.Assembly"/> associated with this context.
        /// </summary>
        /// <value>The <see cref="T:System.Reflection.Assembly"/> associated with this context.</value>
        public Assembly Assembly
        {
            get
            {
                return m_assembly;
            }
        }

        #endregion

        #region public AppDomain AppDomain

        /// <summary>
        /// Gets the <see cref="T:System.AppDomain"/> associated with this context.
        /// </summary>
        /// <value>Gets the <see cref="T:System.AppDomain"/> associated with this context.</value>
        public AppDomain AppDomain
        {
            get
            {
                return m_appDomain;
            }
        }

        #endregion

        #region public Thread Thread

        /// <summary>
        /// Gets the <see cref="T:System.Threading.Thread"/> associated with this context.
        /// </summary>
        /// <value>The <see cref="T:System.Threading.Thread"/> associated with this context.</value>
        public Thread Thread
        {
            get
            {
                return m_thread;
            }
        }

        #endregion

        #region public string OperatingSystem

        /// <summary>
        /// Gets the name of the operating system associated with this context.
        /// </summary>
        /// <value>The name of the operating system associated with this context.</value>
        public string OperatingSystem
        {
            get
            {
                return m_operatingSystem;
            }
        }

        #endregion

        #region public int ProcessorCount

        /// <summary>
        /// Gets the processor count of the machine associated with this context.
        /// </summary>
        /// <remarks>
        /// On machines with multi-core CPU's this value will reflect the total number of cores upon the machine rather than physical processors.
        /// </remarks>
        /// <value>The processor count of the machine associated with this context.</value>
        public int ProcessorCount
        {
            get
            {
                return m_processorCount;
            }
        }

        #endregion 

        #region public Process Process

        /// <summary>
        /// Gets the <see cref="T:System.Diagnostics.Process"/> associated with this context.
        /// </summary>
        /// <value>The <see cref="T:System.Diagnostics.Process"/> associated with this context.</value>
        public Process Process
        {
            get
            {
                return m_process;
            }
        }

        #endregion

        #region public IIdentity IIdentity

        /// <summary>
        /// Gets the <see cref="T:System.Security.Principal.IIdentity"/> of the <see cref="T:System.Security.Principal.IPrincipal"/> associated with this context.
        /// </summary>
        /// <value>The <see cref="T:System.Security.Principal.IIdentity"/> of the <see cref="T:System.Security.Principal.IPrincipal"/> associated with this context.</value>
        public IIdentity IIdentity
        {
            get
            {
                return m_identity;
            }
        }

        #endregion

        #region public string MachineName

        /// <summary>
        /// Gets the machine name associated with this context.
        /// </summary>
        /// <value>The machine name associated with this context.</value>
        public string MachineName
        {
            get
            {
                return m_machineName;
            }
        }

        #endregion

        #region public IPAddress IPAddress

        /// <summary>
        /// Gets the <see cref="T:System.Net.IPAddress"/> of the machine associated with this context.
        /// </summary>
        /// <value>The <see cref="T:System.Net.IPAddress"/> of the machine associated with this context.</value>
        public IPAddress IPAddress
        {
            get
            {
                return m_address;
            }
        }

        #endregion
    }
}
